#include "../include/mystdio.h"
#include "../include/mystring.h"
#include <assert.h>
// #include <unistd.h>
#include <sys/syscall.h>
#include <stdlib.h>

static void itoa(char *target, int i)
{ 
  int j = 0;
  int flag = 0;
  if(i == 0) {
    target[0] = '0';
    target[1] = '\0';
    return ;
  }
  else if(i < 0) {
    flag = 1;
    i *= -1;
  }
  while(i)
  {
    target[j++] = i % 10 + '0';
    i /= 10;
  }
  if(flag == 1) target[j++] = '-';
  target[j] = '\0';
  int mid = j / 2;
  for(i = 0; i < mid; i++)
  {
    char temp = target[i];
    target[i] = target[j - i - 1];
    target[j - i - 1] = temp;
  }
  return ;
}

static int get_presicion(const char *start, int len)
{
  char buf[8];
  _strncpy(buf, start, len);
  buf[len] = '\0';
  return atoi(buf);
}

static void dec_to_hexstr(char *buf, word_t num)
{
  char index[] = "0123456789abcdef";
  char *p = buf;
  *p++ = '0';
  *p++ = 'x';
  int len = sizeof(word_t) * 2;
  p = &buf[len - 1];
  *p-- = '\0';
  while(num)
  {
    *p-- = index[num % 16];
    num /= 16;
  }
  while(p != &buf[1])
  {
    *p-- = index[0];
  }
}

size_t _write(int fd, const void *buf, size_t count)
{
  size_t rt;
  asm volatile (
      "syscall\n\t"
      : "=a" (rt)
      : "0" (SYS_write), "D" (fd), "S" (buf), "d" (count)
  );
  return rt;
}

int _printf(const char *fmt, ...) {
  char buf[1024];
  va_list ap;
  va_start(ap, fmt);
  int rt = _vsprintf(buf, fmt, ap);
  va_end(ap);
  _write(1, buf, _strlen(buf));
  return rt;
}


int _vsprintf(char *out, const char *fmt, va_list ap) {
  char format = ' ';
  char *p = out;
  for(int i = 0; fmt[i] != '\0'; i++)
  {
    if(fmt[i] == '%') //ex: %011d , 12
    {
      i++; //i point to 0
      int start = i; // start point to 0
      int end = start; // end point to 0
      while(!(fmt[end] == 'd' || fmt[end] == 's' || fmt[end] == 'c' || fmt[end] == 'p'))
        end++; //end -> d
      switch (fmt[end])
      {
      case 'd': {
        int tar = va_arg(ap, int);
        char tar_str[64];
        char tar_str_after[64];
        itoa(tar_str, tar); // tar_str = "12";
        if(end != start) {
          if(fmt[start] == '0') 
          {
            format = '0';
            start++; //start point to 1
          }
          int tar_len = _strlen(tar_str); //2
          if(tar_str[0] == '-') tar_len--; //Negative correction
          int presicion = get_presicion(&fmt[start], end - start);
          if(presicion > tar_len)
          {
            char *pa = tar_str_after;
            int delta = presicion - tar_len;
            while(delta--) *(pa++) = format;
            _strcpy(pa, tar_str);
            _strcpy(tar_str, tar_str_after);
          }
        }
        _strcpy(p, tar_str);
        p += _strlen(tar_str);
        i = end;
        break;
      }
      case 'p': {
        word_t addr = (word_t)va_arg(ap, void *);
        char buf[64];
        dec_to_hexstr(buf, addr);
        _strcpy(p, buf);
        p += _strlen(buf);
        break;
      }
      case 's':{
        char *str = va_arg(ap, char *);
        _strcpy(p, str);
        p += _strlen(str);
        break;
      }
      case 'c': {
        char ch = (char)va_arg(ap, int);
        *p = ch;
        p++;
        break;
      }
      default: {
        i--;
        assert(0);//should never come to here
        break;
      }
        break;
      }
    } else {
      *p = fmt[i];
      p++;
    }
  }
  *p = '\0';
  return _strlen(out);
}

int _sprintf(char *out, const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  int rt = _vsprintf(out, fmt, ap);
  va_end(ap);
  return rt;
}

int _snprintf(char *out, size_t n, const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  int rt = _vsnprintf(out, n, fmt, ap);
  va_end(ap);
  return rt;
  return 0;
}

int _vsnprintf(char *out, size_t n, const char *fmt, va_list ap) {
  char buf[n];
  int ret = _vsprintf(buf, fmt, ap);
  return ret;
}

